#include "Avalon/ServiceLocatorTests/MockServiceLocatorDataStore.hpp"
#include <cppunit/extensions/HelperMacros.h>
#include "Avalon/ServiceLocator/Account.hpp"

using namespace Avalon;
using namespace Avalon::IO;
using namespace Avalon::ServiceLocator;
using namespace Avalon::ServiceLocator::Tests;
using namespace Avalon::Threading;
using namespace boost;
using namespace boost::posix_time;
using namespace boost::signals2;
using namespace std;

MockServiceLocatorDataStore::MockServiceLocatorDataStore()
    : m_nextId(0),
      m_isOpen(false) {}

MockServiceLocatorDataStore::~MockServiceLocatorDataStore() {
  for(map<unsigned int, DirectoryEntry*>::iterator i = m_idToEntry.begin();
      i != m_idToEntry.end(); ++i) {
    delete i->second;
  }
}

int MockServiceLocatorDataStore::LoadAccountCount() {
  return static_cast<int>(m_nameToId.size());
}

DirectoryEntry::Tag MockServiceLocatorDataStore::LoadTag(unsigned int id) {
  map<unsigned int, DirectoryEntry*>::const_iterator entryIterator =
    m_idToEntry.find(id);
  DirectoryEntry::Tag tag;
  if(entryIterator != m_idToEntry.end()) {
    tag = entryIterator->second->GetTag();
  }
  return tag;
}

DirectoryEntry::Tag MockServiceLocatorDataStore::LoadParentTag(
    unsigned int id) {
  map<unsigned int, unsigned int>::const_iterator parentIterator =
    m_idToParent.find(id);
  if(parentIterator == m_idToParent.end()) {
    return DirectoryEntry::Tag();
  }
  unsigned int parentId = parentIterator->second;
  if(parentId == -1) {
    return DirectoryEntry::Tag(DirectoryEntry::Type::DIRECTORY, -1, "");
  }
  return LoadTag(parentIterator->second);
}

DirectoryEntry::Tag MockServiceLocatorDataStore::LoadAccountTag(
    const string& name) {
  map<string, unsigned int>::const_iterator idIterator = m_nameToId.find(name);
  if(idIterator == m_nameToId.end()) {
    return DirectoryEntry::Tag();
  }
  return LoadTag(idIterator->second);
}

unsigned int MockServiceLocatorDataStore::CreateDirectory(const string& name,
    const DirectoryEntry::Tag& directory) {
  DirectoryEntry::Tag tag(DirectoryEntry::Type::DIRECTORY, m_nextId, name);
  ++m_nextId;
  Directory* entry = new Directory(tag);
  m_idToEntry.insert(make_pair(tag.m_id, entry));
  m_idToParent.insert(make_pair(tag.m_id, directory.m_id));
  return tag.m_id;
}

void MockServiceLocatorDataStore::DeleteDirectory(unsigned int id) {
  map<unsigned int, DirectoryEntry*>::const_iterator entryIterator =
    m_idToEntry.find(id);
  CPPUNIT_ASSERT(entryIterator != m_idToEntry.end());
  DirectoryEntry* entry = entryIterator->second;
  CPPUNIT_ASSERT(entry->GetType() == DirectoryEntry::Type::DIRECTORY);
  m_idToEntry.erase(id);
  m_idToParent.erase(id);
  delete entry;
}

void MockServiceLocatorDataStore::MoveEntry(unsigned int id,
    unsigned int directory) {
  return;
}

vector<DirectoryEntry::Tag> MockServiceLocatorDataStore::LoadDirectoryEntries(
    unsigned int id) {
  return vector<DirectoryEntry::Tag>();
}

unsigned int MockServiceLocatorDataStore::CreateAccount(const string& name,
    const DirectoryEntry::Tag& directory) {
  DirectoryEntry::Tag tag(DirectoryEntry::Type::ACCOUNT, m_nextId, name);
  ++m_nextId;
  Account* account = new Account(tag);
  m_idToEntry.insert(make_pair(tag.m_id, account));
  m_idToParent.insert(make_pair(tag.m_id, directory.m_id));
  m_nameToId.insert(make_pair(name, tag.m_id));
  m_accountToPassword.insert(make_pair(tag.m_id, ""));
  return tag.m_id;
}

std::string MockServiceLocatorDataStore::LoadPassword(unsigned int account) {
  map<unsigned int, string>::const_iterator passwordIterator =
    m_accountToPassword.find(account);
  if(passwordIterator == m_accountToPassword.end()) {
    return "";
  }
  return passwordIterator->second;
}
 
void MockServiceLocatorDataStore::DeleteAccount(unsigned int id) {
  map<unsigned int, DirectoryEntry*>::const_iterator entryIterator =
    m_idToEntry.find(id);
  CPPUNIT_ASSERT(entryIterator != m_idToEntry.end());
  DirectoryEntry* entry = entryIterator->second;
  CPPUNIT_ASSERT(entry->GetType() == DirectoryEntry::Type::ACCOUNT);
  m_idToEntry.erase(id);
  m_nameToId.erase(entry->GetTag().m_name);
  m_accountToPassword.erase(id);
  m_idToParent.erase(id);
  delete entry;
}

void MockServiceLocatorDataStore::SetPassword(unsigned int account,
    const string& password) {
  map<unsigned int, string>::iterator accountIterator =
    m_accountToPassword.find(account);
  if(accountIterator == m_accountToPassword.end()) {
    return;
  }
  accountIterator->second = password;
}

map<unsigned int, Directory::Permission>
    MockServiceLocatorDataStore::LoadPermissions(unsigned int id) {
  map<unsigned int, map<unsigned int, Directory::Permission> >::const_iterator
    permissionsIterator = m_permissions.find(id);
  if(permissionsIterator == m_permissions.end()) {
    return map<unsigned int, Directory::Permission>();
  }
  return permissionsIterator->second;
}

Directory::Permission MockServiceLocatorDataStore::LoadAccountPermissions(
    unsigned int account, unsigned int directory) {
  return m_permissions[account][directory];
}

void MockServiceLocatorDataStore::SetPermissions(unsigned int account,
    unsigned int directory, Directory::Permission permissions) {
  m_permissions[account][directory] = permissions;
}

vector<string> MockServiceLocatorDataStore::GetUnavailableNames() {
  return m_unavailableNames;
}

bool MockServiceLocatorDataStore::IsNameAvailable(const string& name) {
  return find(m_unavailableNames.begin(), m_unavailableNames.end(), name) ==
    m_unavailableNames.end();
}

void MockServiceLocatorDataStore::AddUnavailableName(const string& name) {
  CPPUNIT_ASSERT(IsNameAvailable(name));
  m_unavailableNames.push_back(name);
}

void MockServiceLocatorDataStore::RemoveUnavailableName(const string& name) {
  CPPUNIT_ASSERT(!IsNameAvailable(name));
  m_unavailableNames.erase(find(m_unavailableNames.begin(),
    m_unavailableNames.end(), name));
}

void MockServiceLocatorDataStore::Open() {
  if(m_isOpen) {
    return;
  }
  m_isOpen = true;
}

bool MockServiceLocatorDataStore::IsOpen() const {
  return m_isOpen;
}

void MockServiceLocatorDataStore::Close() {
  if(!m_isOpen) {
    return;
  }
  m_isOpen = false;
  boost::shared_ptr<ClosedSlot> closedSlot = m_closedSlot;
  (*closedSlot)();
}

void MockServiceLocatorDataStore::SetClosedSlot(const ClosedSlot& slot,
    Out<bool> isOpen) {
  m_closedSlot.reset(new ClosedSlot(slot));
  *isOpen = m_isOpen;
}
